using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Reflection;
using System.Xml.Serialization;
using IKE.EntryLib;
using System.Collections;
using System.IO;
using IKE.LCAFormat.Properties;

namespace IKE.LCAFormat.ILCD
{
    public abstract class AutoSerializeBase
    {
        public static Dictionary<string, XNamespace> DictNamespaces = new Dictionary<string, XNamespace>();
        /// <summary>
        /// 序列化到XElement
        /// </summary>
        /// <param name="parentFieldName"></param>
        /// <returns></returns>
        public XElement ToElement(XName parentFieldName)
        {
            XElement ele = new XElement(parentFieldName);
          
                var fields = this.GetType().GetFields();
                XName eleName;
                foreach (var field in DisplayUIAttribute.GetSortedMembers(fields))
                {
                    object value = field.GetValue(this);
                    if (value == null) continue;
                    eleName = GetRealElementName(field);
                    bool isEnumValue;
                    int enumValueClassId = TryGetEnumValueClassId(field, out isEnumValue);
                    if (value is AutoSerializeBase)
                    {
                        ele.Add((value as AutoSerializeBase).ToElement(eleName));
                    }
                    else if (value is IList)
                    {
                        var lst = value as IList;
                        foreach (var item in lst)
                        {
                            if (item is AutoSerializeBase)
                            {
                                ele.Add(((AutoSerializeBase)item).ToElement(eleName));
                            }
                            else if (item is int)
                            {
                                if (isEnumValue)
                                    ele.Add(new XElement(eleName, GetEnumValueEnglishName((int)item, enumValueClassId)));
                                else
                                    ele.Add(new XElement(eleName, item));
                            }
                            else
                                throw new ArgumentException();
                        }
                    }
                    else
                    {
                        if (isEnumValue)
                        {
                            value = GetEnumValueEnglishName((int)value, enumValueClassId);
                        }
                        if (IsToAttribute(field))
                        {
                            if (eleName.Namespace == AutoSerializeBase.DictNamespaces["Flow"])
                                ele.SetAttributeValue(eleName.LocalName, value);
                            else
                                ele.SetAttributeValue(eleName, value);
                        }
                        else if (IsToText(field))
                        {
                            ele.Value = value.ToString();
                        }
                        else
                            ele.Add(new XElement(eleName, value));
                    }
                }
           
                return ele;
         
           
        }

        public void FromElement(XElement parentElement)
        {
            var fields = this.GetType().GetFields();
            XName eleName;
            foreach (var field in fields)
            {
                eleName = GetRealElementName(field);
                bool isEnumValue;
                int enumValueClassId = TryGetEnumValueClassId(field, out isEnumValue);
                if (field.FieldType.BaseType == typeof(AutoSerializeBase))
                {
                    var ele = parentElement.Element(eleName);
                    if (ele == null)
                        continue;
                    var value = (AutoSerializeBase)Activator.CreateInstance(field.FieldType);
                    value.FromElement(ele);
                    field.SetValue(this, value);
                }
                else if (field.FieldType == typeof(List<int>))
                {
                    var eles = parentElement.Elements(eleName);
                    if (eles.Count() == 0)
                        continue;
                    var lst = new List<int>();
                    foreach (var ele in eles)
                    {
                        if (isEnumValue)
                            lst.Add(GetEnumValueByName(ele.Value, enumValueClassId));
                        else
                            lst.Add(int.Parse(ele.Value));
                    }
                    if (lst.Count > 0) field.SetValue(this, lst);
                }
                else if (field.FieldType == typeof(int))
                {
                    int value;
                    if (IsToAttribute(field))
                    {
                        XAttribute att;
                        if (eleName.Namespace == AutoSerializeBase.DictNamespaces["Flow"].NamespaceName)
                            att = parentElement.Attribute(eleName.LocalName);
                        else
                            att = parentElement.Attribute(eleName);
                        if (att == null)
                            continue;
                        if (isEnumValue)
                            value = GetEnumValueByName(att.Value, enumValueClassId);
                        else
                            value = int.Parse(att.Value);
                    }
                    else
                    {
                        var ele = parentElement.Element(eleName);
                        if (ele == null)
                            continue;
                        if (isEnumValue)
                            value = GetEnumValueByName(ele.Value, enumValueClassId);
                        else
                            value = int.Parse(ele.Value);
                    }
                    field.SetValue(this, value);
                }
                else if (field.FieldType == typeof(string))
                {
                    if (IsToAttribute(field))
                    {
                        XAttribute att;
                        if (eleName.Namespace == AutoSerializeBase.DictNamespaces["Flow"])
                            att = parentElement.Attribute(eleName.LocalName);
                        else
                            att = parentElement.Attribute(eleName);
                        if (att == null)
                            continue;
                        field.SetValue(this, att.Value);
                    }
                    else if (IsToText(field))
                    {
                        field.SetValue(this, parentElement.Value);
                    }
                    else
                    {
                        var ele = parentElement.Element(eleName);
                        if (ele == null) continue;
                        field.SetValue(this, ele.Value);
                    }
                }
                else if (field.FieldType == typeof(Guid))
                {
                    if (IsToAttribute(field))
                    {
                        XAttribute att;
                        if (eleName.Namespace == AutoSerializeBase.DictNamespaces["Flow"])
                            att = parentElement.Attribute(eleName.LocalName);
                        else
                            att = parentElement.Attribute(eleName);
                        if (att == null)
                            continue;
                        field.SetValue(this, new Guid(att.Value));
                    }
                    else
                    {
                        var ele = parentElement.Element(eleName);
                        if (ele == null) continue;
                        field.SetValue(this, new Guid(ele.Value));
                    }
                }
                else if (field.FieldType == typeof(DateTime))
                {
                    var ele = parentElement.Element(eleName);
                    if (ele == null) continue;
                    field.SetValue(this, DateTime.Parse(ele.Value));
                }
                else if (field.FieldType == typeof(decimal))
                {
                    var ele = parentElement.Element(eleName);
                    if (ele == null) continue;
                    field.SetValue(this, (decimal)double.Parse(ele.Value));
                }
                else if (field.FieldType == typeof(bool))
                {
                    var ele = parentElement.Element(eleName);
                    if (ele == null) continue;
                    field.SetValue(this, bool.Parse(ele.Value));
                }
                else if (field.FieldType == typeof(double))
                {
                    var ele = parentElement.Element(eleName);
                    if (ele == null) continue;
                    field.SetValue(this, double.Parse(ele.Value));
                }
                else//List<AutoSerializeBase>
                {
                    var eles = parentElement.Elements(eleName);
                    var lst = (IList)Activator.CreateInstance(field.FieldType);
                    foreach (var ele in eles)
                    {
                        var genericType = field.FieldType.GetGenericArguments()[0];
                        var value = (AutoSerializeBase)Activator.CreateInstance(genericType);
                        value.FromElement(ele);
                        lst.Add(value);
                    }
                    if (lst.Count > 0) field.SetValue(this, lst);
                }
            }
        }

        private static XDocument _EnumValuesXDoc;
        private static XDocument EnumValuesXDoc
        {
            get
            {
                if (_EnumValuesXDoc == null)
                {
                    _EnumValuesXDoc = XDocument.Parse(Resources.EnumValueClasses_ILCD);
                }
                return _EnumValuesXDoc;
            }
        }
        private static string GetEnumValueEnglishName(int value, int enumValueClassId)
        {
            var evc = EnumValuesXDoc.Descendants("EnumValueClass").First(o => int.Parse(o.Element("ID").Value) == enumValueClassId);
            value = value == 0 ? evc.Descendants("EnumValue").Count() : value;//如果为0，那么总是最后一个（未设置）
            var ev = evc.Descendants("EnumValue").First(o => int.Parse(o.Element("ID").Value) == value);
            var name = ev.Element("Names").Elements("item").First(o => o.Attribute("key").Value == "en");
            return name.Value;
        }

        private static int GetEnumValueByName(string name, int enumValueClassId)
        {
            var evc = EnumValuesXDoc.Descendants("EnumValueClass").First(o => int.Parse(o.Element("ID").Value) == enumValueClassId);
            var query = from item in evc.Descendants("item")
                        where item.Parent.Name == "Names"
                        && item.Value == name
                        select item;
            var ev = query.First().Parent.Parent;
            return int.Parse(ev.Element("ID").Value);
        }

        private int TryGetEnumValueClassId(FieldInfo field, out bool has)
        {
            var atts = field.GetCustomAttributes(typeof(EnumValuesAttribute), false);
            if (atts.Length > 0)
            {
                has = true;
                return (atts[0] as EnumValuesAttribute).ClassID;
            }
            else
            {
                has = false;
                return -1;
            }
        }

        private bool IsIList(FieldInfo field)
        {
            var baseType = field.FieldType.GetInterface("System.Collections.IList");
            if (baseType == null)
                return false;
            else
                return true;
        }

        private bool IsToAttribute(FieldInfo field)
        {
            var atts = field.GetCustomAttributes(typeof(XmlAttributeAttribute), false);
            if (atts.Length > 0)
                return true;
            else
                return false;
        }
        private bool IsToText(FieldInfo field)
        {

            var atts = field.GetCustomAttributes(typeof(XmlTextAttribute), false);
            if (atts.Length > 0)
                return true;
            else
                return false;
        }
        private XName GetRealElementName(FieldInfo field)
        {
            var atts = field.GetCustomAttributes(typeof(XmlnsAttribute), false);
            if (atts.Length > 0)
            {
                string xnpName = (atts[0] as XmlnsAttribute).Name;
                if (xnpName == "xmlns")
                    return XNamespace.Xmlns + field.Name;
                else if (xnpName == "xml")
                    return XNamespace.Xml + field.Name;
                else
                    return AutoSerializeBase.DictNamespaces[xnpName] + field.Name;
            }
            else
                return AutoSerializeBase.DictNamespaces["Flow"] + field.Name;
            //return field.Name;
        }
    }
}
